#include "SwfUI.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string>
#include <objbase.h>
#include <assert.h>
#import "PROGID:ShockwaveFlash.ShockwaveFlash" named_guids

typedef HRESULT (__stdcall *GetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID * ppv); 

namespace SwfUI
{
	HMODULE flashLib = 0;
	std::wstring baseDir;
	Control* focusedControl = 0;
	HHOOK getMsgHook = 0;

	namespace detail
	{
		LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam);
	}

	void initialize()
	{
		CoInitialize(0);
		flashLib = LoadLibrary(L"flash.ocx");

		wchar_t buffer[MAX_PATH];
		GetCurrentDirectory(MAX_PATH,buffer);
		baseDir = buffer;
		baseDir += L"\\";

		HINSTANCE hInstance = GetModuleHandle(0);
		getMsgHook = SetWindowsHookEx(WH_GETMESSAGE, &detail::GetMessageProc, hInstance, GetCurrentThreadId());
	}

	void shutdown()
	{
		if(flashLib)
			FreeLibrary(flashLib);

		UnhookWindowsHookEx(getMsgHook);

		CoUninitialize();
	}

	void setFocus(Control* control)
	{
		focusedControl = control;
	}

	Control* getFocus()
	{
		return focusedControl;
	}

	namespace detail
	{
		void createFlashObject(ShockwaveFlashObjects::IShockwaveFlash** handle)
		{
			IOleObject* oleObject = 0;
			// Try to load from user-supplied Flash OCX first
			if(flashLib)
			{
				IClassFactory* factory = 0;
				GetClassObject getClassFunc = (GetClassObject)GetProcAddress(flashLib, "DllGetClassObject");
				HRESULT result = getClassFunc(ShockwaveFlashObjects::CLSID_ShockwaveFlash, IID_IClassFactory, (void**)&factory);
				if(SUCCEEDED(result))
				{
					factory->CreateInstance(NULL, IID_IOleObject, (void**)&oleObject);
					factory->Release();	
				}
			}

			// If we still don't have the object, try loading from registry
			if(!oleObject)
			{
				HRESULT result = CoCreateInstance(ShockwaveFlashObjects::CLSID_ShockwaveFlash, 0, CLSCTX_INPROC_SERVER, IID_IOleObject, (void**)&oleObject);
				assert(SUCCEEDED(result));
			}

			oleObject->QueryInterface(handle);
			oleObject->Release();
		}

		const std::wstring& getBaseDir()
		{
			return baseDir;
		}

		LRESULT CALLBACK GetMessageProc(int nCode, WPARAM wParam, LPARAM lParam)
		{
			if(nCode == HC_ACTION)
			{
				MSG *msg = (MSG*)lParam;
				if(wParam & PM_REMOVE)
				{
					switch(msg->message)
					{
					case WM_KEYDOWN:
					case WM_KEYUP:
					case WM_CHAR:
					case WM_DEADCHAR:
					case WM_SYSKEYDOWN:
					case WM_SYSKEYUP:
					case WM_SYSDEADCHAR:
					case WM_SYSCHAR:
					case WM_IME_CHAR:
					case WM_IME_COMPOSITION:
					case WM_IME_COMPOSITIONFULL:
					case WM_IME_CONTROL:
					case WM_IME_ENDCOMPOSITION:
					case WM_IME_KEYDOWN:
					case WM_IME_KEYUP:
					case WM_IME_NOTIFY:
					case WM_IME_REQUEST:
					case WM_IME_SELECT:
					case WM_IME_SETCONTEXT:
					case WM_IME_STARTCOMPOSITION:
					case WM_HELP:
					case WM_CANCELMODE: 
						{
							if(focusedControl)
							{
								focusedControl->injectKey(msg);
							}
						}
					}
				}
			}

			return CallNextHookEx(getMsgHook, nCode, wParam, lParam);
		}
	}
}